package cn.rayland.library.utils;

import android.util.Log;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * Created by gw on 2016/7/12.
 */

public class StlEditor {
    private static final String TAG = StlEditor.class.getSimpleName();

    public static void editStlFile(File stlFile, File destFile, float xMove, float yMove, float zMove, float scale, float zRotate){
    	Log.i(TAG, "stl = "+stlFile.getAbsolutePath()+
    				"\ndest = "+destFile.getAbsolutePath()+
    				"\nx_offset = "+xMove+
    				"\ny_offset = "+yMove+
    				"\nz_offset = "+zMove+
    				"\nscale = "+scale+
    				"\nz_rotate = "+zRotate);
        if(isTextFile(stlFile)){
            Log.i(TAG, "is ascii file");
            editAsciiFile(stlFile, destFile, xMove, yMove, zMove, scale, zRotate);
        }else{
            Log.i(TAG, "is binary file");
            editBinaryFile(stlFile, destFile, xMove, yMove, zMove, scale, zRotate);
        }
        Log.i(TAG, "new stl created success");
    }

    private static void editBinaryFile(File stlFile, File destFile, float xMove, float yMove, float zMove, float scale, float zRotate) {
        BufferedInputStream fis = null;
        BufferedOutputStream fos = null;
        try {
            fis = new BufferedInputStream(new FileInputStream(stlFile));
            fos = new BufferedOutputStream(new FileOutputStream(destFile));
            byte[] header = new byte[84];
            fis.read(header);
            fos.write(header);
            byte[] facet = new byte[50];
            while(fis.read(facet) != -1){
                float[] normalArray = new float[3];
                float[] vertexArray = new float[9];

                normalArray[0] = Float.intBitsToFloat(getIntByLittleEndian(facet, 0));
                normalArray[1] = Float.intBitsToFloat(getIntByLittleEndian(facet, 4));
                if(zRotate != 0) {
                    float normalX = (float) ((normalArray[0] * Math.cos(zRotate * Math.PI / 180)) - (normalArray[1] * Math.sin(zRotate * Math.PI / 180)));
                    float normalY = (float) ((normalArray[0] * Math.sin(zRotate * Math.PI / 180)) + (normalArray[1] * Math.cos(zRotate * Math.PI / 180)));
                    normalArray[0] = normalX;
                    normalArray[1] = normalY;
                    byte[] xbytes = toLH(Float.floatToRawIntBits(normalArray[0]));
                    byte[] ybytes = toLH(Float.floatToRawIntBits(normalArray[1]));
                    System.arraycopy(xbytes, 0, facet, 0, xbytes.length);
                    System.arraycopy(ybytes, 0, facet, 4, ybytes.length);
                }

                for (int i = 0; i < 3; i++) {
                    vertexArray[i * 3] = Float.intBitsToFloat(getIntByLittleEndian(facet, (i * 12) + 12));
                    vertexArray[(i * 3) + 1] = Float.intBitsToFloat(getIntByLittleEndian(facet, (i * 12) + 16));
                    vertexArray[(i * 3) + 2] = Float.intBitsToFloat(getIntByLittleEndian(facet, (i * 12) + 20));

                    if(scale > 0 && scale != 1){
                        vertexArray[i * 3] *= scale;
                        vertexArray[(i * 3) + 1] *= scale;
                        vertexArray[(i * 3) + 2] *= scale;
                    }

                    if(zRotate != 0){
                        float x = (float) ((vertexArray[i * 3] * Math.cos(zRotate*Math.PI/180)) - (vertexArray[(i * 3) + 1] * Math.sin(zRotate*Math.PI/180)));
                        float y = (float) ((vertexArray[i * 3] * Math.sin(zRotate*Math.PI/180)) + (vertexArray[(i * 3) + 1] * Math.cos(zRotate*Math.PI/180)));
                        vertexArray[i * 3] = x;
                        vertexArray[(i * 3) + 1] = y;
                    }
                    
                    vertexArray[i * 3] += xMove;
                    vertexArray[(i * 3) + 1] += yMove;
                    vertexArray[(i * 3) + 2] += zMove;
                    
                    byte[] xbytes = toLH(Float.floatToRawIntBits(vertexArray[i * 3]));
                    byte[] ybytes = toLH(Float.floatToRawIntBits(vertexArray[(i * 3) + 1]));
                    byte[] zbytes = toLH(Float.floatToRawIntBits(vertexArray[(i * 3) + 2]));
                    System.arraycopy(xbytes, 0, facet, (i * 12) + 12, xbytes.length);
                    System.arraycopy(ybytes, 0, facet, (i * 12) + 16, ybytes.length);
                    System.arraycopy(zbytes, 0, facet, (i * 12) + 20, zbytes.length);
                }
                fos.write(facet);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void editAsciiFile(File stlFile, File destFile, float xMove, float yMove, float zMove, float scale, float zRotate) {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            br = new BufferedReader(new FileReader(stlFile));
            bw = new BufferedWriter(new FileWriter(destFile));
            String str;
            while((str = br.readLine()) != null){
            	str = str.trim();
                if(str.startsWith("facet normal ")){
                    str = str.replaceFirst("(^facet normal)([ \\f\\r\\t\\n]+)", "");
                    String[] normalArray = str.split(" ");
                    float x = Float.parseFloat(normalArray[0]);
                    float y = Float.parseFloat(normalArray[1]);
                    float z = Float.parseFloat(normalArray[2]);
                    if (zRotate != 0) {
                        float normalX = (float) ((x * Math.cos(zRotate * Math.PI / 180)) - (y * Math.sin(zRotate * Math.PI / 180)));
                        float normalY = (float) ((x * Math.sin(zRotate * Math.PI / 180)) + (y * Math.cos(zRotate * Math.PI / 180)));
                        x = normalX;
                        y = normalY;
                    }
                   str = "facet normal " + x + " " + y + " " + z;
                }

                if(str.startsWith("vertex ")){
                    str = str.replaceFirst("(^vertex)([ \\f\\r\\t\\n]+)", "");
                    String[] normalArray = str.split(" ");
                    float x = Float.parseFloat(normalArray[0]);
                    float y = Float.parseFloat(normalArray[1]);
                    float z = Float.parseFloat(normalArray[2]);

                    if(scale > 0 && scale != 1){
                        x *= scale;
                        y *= scale;
                        z *= scale;
                    }
                    
                    if(zRotate != 0){
                        float vertexX = (float) ((x * Math.cos(zRotate*Math.PI/180)) - (y * Math.sin(zRotate*Math.PI/180)));
                        float vertexY = (float) ((x * Math.sin(zRotate*Math.PI/180)) + (y * Math.cos(zRotate*Math.PI/180)));
                        x = vertexX;
                        y = vertexY;
                    }
                    
                    x += xMove;
                    y += yMove;
                    z += zMove;

                    str = "vertex " + x + " " + y + " " + z;
                }

                bw.write(str + "\n");
            }


        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bw != null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * check file type, text or binary
     * @param stlFile
     * @return
     */
    private static boolean isTextFile(File stlFile){
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(stlFile));
            br.skip(80);
            int line = 0;
            String buffer;
            while((buffer = br.readLine()) != null && line < 5){
                line ++;
                if(buffer.contains("facet")||buffer.contains("outer")||buffer.contains("vertex")||buffer.contains("end")){
                    return true;
                }
            }
            br.close();
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if(br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    /**
     * convert LittleEdian byte[] to int
     * @param bytes
     * @param offset
     * @return
     */
    private static int getIntByLittleEndian(byte[] bytes, int offset){
        return (0xff & bytes[offset]) | ((0xff & bytes[offset + 1]) << 8) | ((0xff & bytes[offset + 2]) << 16) | ((0xff & bytes[offset + 3]) << 24);
    }

    /**
     * convert int to LittleEdian byte[]
     * @param n int
     * @return byte[]
     */
    public static byte[] toLH(int n) {
        byte[] b = new byte[4];
        b[0] = (byte) (n & 0xff);
        b[1] = (byte) (n >> 8 & 0xff);
        b[2] = (byte) (n >> 16 & 0xff);
        b[3] = (byte) (n >> 24 & 0xff);
        return b;
    }
}
